package hyl.base.zk;

import java.io.IOException;
import java.util.List;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;

import hyl.core.struct.MyCollect;


/**
 * 协作方式
 * 
 * 不同于 储备方式, 在协作方式下 备用节点和主节点一起工作, 如果备用节点异常,就通知其他节点<br>
 * 
 * 其他节点接收到离线信号进行处理时<br>
 * 
 * 原理： <br>
 * 1 连接zk，并注册监听父节点<br>
 * 2 离线通知zk <br>
 * 3 zk通过watch 通知其他监听者<br>
 * 4 监听者收到信号进行处理<br>
 * 5 并且继续绑定zk监听父节点
 * 
 * 
 * @author 37798955@qq.com
 *
 */
public class ZkCooperation {
	// private static final Logger logger =
	// LoggerFactory.getLogger(ZkCooperation.class);
	MyZK _zk = null;
	/** projectDir 指向 工程 */
	String projectDir = null;
	/** processPath 指向 工程下的当前进程 */
	String processPath = null;
	/**
	 * processID 指向 工程下的当前进程id processID=zk 临时节点的编号
	 */
	String processID = null;
	/**
	 * 专门用于存放项目集下项目的节点路径
	 */
	// String projectNode = null;
	/**
	 * projectId=项目名称
	 */
	String projectId = null;

	public MyZK getZK() {
		return _zk;
	}

	/**
	 * 
	 * @param 项目集  规定在 zk 上面的目录节点
	 * @param zk组件 zk连接组件
	 * @param 处理事件 监听后的处理动作
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public ZkCooperation(String 项目集, MyZK zk组件, IZKEvent 处理事件) throws IOException, InterruptedException {
		projectDir = 项目集;
		// projectNode = projectDir + "/nodes";
		_zk = zk组件;
		// _zk.createPNodes(projectDir, null);// 如果父节点不存在 补上
		_zk.createPNodes(projectDir, null);// 如果父节点不存在 补上
		_handler = 处理事件;
		start();
	}

	/**
	 * zk上的临时编号
	 * 
	 * @return
	 */
	public String getPID() {
		return this.processID;
	}

	public String getProcessPath() {
		return processPath;
	}

	void setProcessPath(String processPath) {
		this.processPath = processPath;
		this.processID = processPath.substring(projectDir.length() + 1);

		// System.out.println("projectDir=" + projectDir);
		// System.out.println("processPath=" + processPath);
		// System.out.println("processID=" + processID);
	}

	public List<String> getChildren() {
		return _zk.getChildren(projectDir);
	}

	void start() {
		// 创建获取父节点
		if (null == processID) {
			String path = _zk.createTNode(projectDir, null); // 创建代表程序的临时节点
			setProcessPath(path);// 存放路径
			// 锁住节点并等待 启用通知或切换通知
			// 监听父节点
			_子节点集合 = _zk.watchChildren(projectDir, zkWatcher);
		}
	}

	List<String> _子节点集合 = null;
	ChangedWatcher zkWatcher = new ChangedWatcher();
	/** 进程主函数,从外部通过runnable接口注入 */
	public IZKEvent _handler = null;

	/**
	 * 
	 * 离线状态监听器<br>
	 * 
	 * 如果缺少父节点创建父节点 然后在父节点下面创建临时节点 创建临时节点
	 * 
	 * @author 37798955@qq.com
	 *
	 */
	class ChangedWatcher implements Watcher {

		/*
		 * watchedEvent 被监听的节点事件 如果当前 监听到 某个节点 离线 那么 1 判断是否需要复活,如果是第一个节点就复活 2
		 * 如果不需要复活,重新监听前一个人
		 */
		@Override
		public void process(WatchedEvent event) {

//			if (event.getType() == EventType.NodeDeleted && _fun.containsKey(EventType.NodeDeleted)) {
//			} else if (event.getType() == EventType.NodeDataChanged && _fun.containsKey(EventType.NodeDataChanged)) {
//			} else if (event.getType() == EventType.NodeChildrenChanged
//					&& _fun.containsKey(EventType.NodeChildrenChanged)) {
//			} else if (event.getType() == EventType.None && _fun.containsKey(EventType.None)) {
//				// logger.error("主工作程序异常停止,准备激活当前程序");
//			}

			// System.out.println("为啥没有发现呢:" + event.toString());

			// 再次监听子节点改变

			List<String> 离网 = null;
			List<String> 新增 = null;
			List<String> 交集 = null;
			List<String> 在网 = _zk.watchChildren(projectDir, zkWatcher);
			if (_子节点集合 == null) {
				新增 = 在网;
			} else {
				离网 = MyCollect.get不在L2中(_子节点集合, 在网);
				新增 = MyCollect.get不在L2中(在网, _子节点集合);
				交集 = MyCollect.get交集(在网, _子节点集合);
			}
			_子节点集合 = 在网;
			_handler.run(event, 在网, 交集, 新增, 离网);

			// 激活指令
		}

	}

}
